package com.clouddms.service.transfer;

import com.clouddms.service.common.IService;
import com.clouddms.service.common.ServiceStatus;
import com.clouddms.service.security.EncryptionService;
import com.clouddms.service.security.EncryptionResult;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FilenameUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;

/**
 * 文件传输服务
 * 负责文件上传、下载和断点续传功能
 *
 * @author Cloud-DMS Team
 * @version 1.0.0
 */
@Slf4j
@Service
public class FileTransferService implements IService {

    @Autowired
    private EncryptionService encryptionService;

    @Autowired
    private ChunkUploadManager chunkUploadManager;

    @Value("${clouddms.storage.base-path:uploads/}")
    private String basePath;

    @Value("${clouddms.storage.max-file-size:52428800}")
    private long maxFileSize;

    @Value("${clouddms.storage.allowed-extensions:jpg,jpeg,png,gif,pdf,doc,docx,xls,xlsx,ppt,pptx,txt,zip,rar}")
    private String allowedExtensions;

    private static final int BUFFER_SIZE = 8192;
    private static final String TEMP_SUFFIX = ".tmp";

    // === ICloudDmsService 接口实现 ===

    @Override
    public String getServiceName() {
        return "networkTransfer";
    }

    @Override
    public String getDisplayName() {
        return "网络传输模块";
    }

    @Override
    public String getDescription() {
        return "基于Spring Boot实现文件上传下载，支持断点续传，提供稳定的网络传输服务";
    }

    @Override
    public ServiceStatus getStatus() {
        return ServiceStatus.DEBUGGING;
    }

    @Override
    public int getPriority() {
        return 40; // 网络传输优先级
    }

    // === 智能上传方法 ===

    /**
     * 智能文件上传（自动选择普通上传或分块上传）
     *
     * @param file 上传的文件
     * @param userId 用户ID
     * @return 上传结果
     */
    public UploadResult smartUploadFile(MultipartFile file, Long userId) {
        try {
            log.info("开始智能上传文件: {}, 用户ID: {}, 大小: {} bytes",
                    file.getOriginalFilename(), userId, file.getSize());

            // 验证文件
            UploadResult validation = validateFile(file);
            if (!validation.isSuccess()) {
                return validation;
            }

            // 根据文件大小选择上传策略
            if (chunkUploadManager.shouldUseChunkedUpload(file.getSize())) {
                log.info("使用分块上传策略: fileSize={}", file.getSize());
                return handleChunkedUpload(file, userId);
            } else {
                log.info("使用普通上传策略: fileSize={}", file.getSize());
                return uploadFile(file, userId);
            }

        } catch (Exception e) {
            log.error("智能文件上传失败: {}", e.getMessage(), e);
            return UploadResult.failure("智能上传失败: " + e.getMessage());
        }
    }

    /**
     * 处理分块上传
     */
    private UploadResult handleChunkedUpload(MultipartFile file, Long userId) {
        try {
            // 初始化上传会话
            UploadSession session = chunkUploadManager.initializeUpload(
                file.getOriginalFilename(), file.getSize(), userId);

            // 读取文件并分块上传
            byte[] fileData = file.getBytes();
            int chunkSize = session.getChunkSize();
            int totalChunks = session.getTotalChunks();

            for (int i = 0; i < totalChunks; i++) {
                int start = i * chunkSize;
                int end = Math.min(start + chunkSize, fileData.length);
                byte[] chunkData = new byte[end - start];
                System.arraycopy(fileData, start, chunkData, 0, end - start);

                ChunkUploadResult chunkResult = chunkUploadManager.uploadChunk(
                    session.getUploadId(), i, chunkData);

                if (!chunkResult.isSuccess()) {
                    return UploadResult.failure("分块上传失败: " + chunkResult.getMessage());
                }

                if (chunkResult.isCompleted()) {
                    // 文件合并完成，处理加密和元数据
                    return handleChunkedUploadCompletion(session, chunkResult);
                }
            }

            return UploadResult.failure("分块上传未完成");

        } catch (Exception e) {
            log.error("分块上传处理失败: {}", e.getMessage(), e);
            return UploadResult.failure("分块上传失败: " + e.getMessage());
        }
    }

    /**
     * 处理分块上传完成后的操作
     */
    private UploadResult handleChunkedUploadCompletion(UploadSession session, ChunkUploadResult chunkResult) {
        try {
            String finalFilePath = chunkResult.getFinalFilePath();
            String originalName = session.getFileName();
            String storedName = Paths.get(finalFilePath).getFileName().toString();

            // 计算文件哈希
            String fileHash = encryptionService.calculateMD5(finalFilePath);

            // 检查是否需要加密
            boolean needsEncryption = encryptionService.needsEncryption(originalName);
            boolean encrypted = false;

            if (needsEncryption) {
                String encryptedPath = finalFilePath + ".enc";
                EncryptionResult encResult = encryptionService.encryptFile(finalFilePath, encryptedPath, session.getUserId());
                
                if (encResult.isSuccess()) {
                    // 删除原文件，使用加密文件
                    Files.deleteIfExists(Paths.get(finalFilePath));
                    Files.move(Paths.get(encryptedPath), Paths.get(finalFilePath));
                    encrypted = true;
                    fileHash = encResult.getEncryptedHash();
                    log.info("分块上传文件已加密: {}", finalFilePath);
                } else {
                    log.warn("分块上传文件加密失败，保留原文件: {}", finalFilePath);
                }
            }

            // 构建结果
            UploadResult result = UploadResult.success("分块文件上传成功");
            result.setOriginalName(originalName);
            result.setStoredName(storedName);
            result.setFilePath(finalFilePath);
            result.setFileSize(session.getFileSize());
            result.setContentType("application/octet-stream");
            result.setFileHash(fileHash);
            result.setEncrypted(encrypted);
            result.setUploadId(session.getUploadId());

            log.info("分块文件上传完成: uploadId={}, finalPath={}", 
                session.getUploadId(), finalFilePath);
            return result;

        } catch (Exception e) {
            log.error("分块上传完成处理失败: {}", e.getMessage(), e);
            return UploadResult.failure("分块上传完成处理失败: " + e.getMessage());
        }
    }

    // === 原有的业务方法 ===

    /**
     * 文件上传结果
     */
    public static class UploadResult {
        private boolean success;
        private String message;
        private String originalName;
        private String storedName;
        private String filePath;
        private long fileSize;
        private String contentType;
        private String fileHash;
        private boolean encrypted;
        private String uploadId;

        // 构造函数和getter/setter
        public UploadResult() {}

        public static UploadResult success(String message) {
            UploadResult result = new UploadResult();
            result.success = true;
            result.message = message;
            return result;
        }

        public static UploadResult failure(String message) {
            UploadResult result = new UploadResult();
            result.success = false;
            result.message = message;
            return result;
        }

        // Getters and Setters
        public boolean isSuccess() { return success; }
        public void setSuccess(boolean success) { this.success = success; }
        public String getMessage() { return message; }
        public void setMessage(String message) { this.message = message; }
        public String getOriginalName() { return originalName; }
        public void setOriginalName(String originalName) { this.originalName = originalName; }
        public String getStoredName() { return storedName; }
        public void setStoredName(String storedName) { this.storedName = storedName; }
        public String getFilePath() { return filePath; }
        public void setFilePath(String filePath) { this.filePath = filePath; }
        public long getFileSize() { return fileSize; }
        public void setFileSize(long fileSize) { this.fileSize = fileSize; }
        public String getContentType() { return contentType; }
        public void setContentType(String contentType) { this.contentType = contentType; }
        public String getFileHash() { return fileHash; }
        public void setFileHash(String fileHash) { this.fileHash = fileHash; }
        public boolean isEncrypted() { return encrypted; }
        public void setEncrypted(boolean encrypted) { this.encrypted = encrypted; }
        public String getUploadId() { return uploadId; }
        public void setUploadId(String uploadId) { this.uploadId = uploadId; }
    }

    /**
     * 上传文件
     *
     * @param file 上传的文件
     * @param userId 用户ID
     * @return 上传结果
     */
    public UploadResult uploadFile(MultipartFile file, Long userId) {
        try {
            log.info("开始上传文件: {}, 用户ID: {}, 大小: {} bytes",
                    file.getOriginalFilename(), userId, file.getSize());

            // 验证文件
            UploadResult validation = validateFile(file);
            if (!validation.isSuccess()) {
                return validation;
            }

            // 生成存储信息
            String originalName = file.getOriginalFilename();
            String extension = FilenameUtils.getExtension(originalName);
            String storedName = generateStoredName(extension);
            String userDir = createUserDirectory(userId);
            String filePath = userDir + File.separator + storedName;

            // 保存文件
            Path targetPath = Paths.get(filePath);
            Files.copy(file.getInputStream(), targetPath, StandardCopyOption.REPLACE_EXISTING);

            // 计算文件哈希
            String fileHash = encryptionService.calculateMD5(filePath);

            // 检查是否需要加密
            boolean needsEncryption = encryptionService.needsEncryption(originalName);
            boolean encrypted = false;

            if (needsEncryption) {
                String encryptedPath = filePath + ".enc";
                if (encryptionService.encryptFile(filePath, encryptedPath)) {
                    // 删除原文件，使用加密文件
                    Files.deleteIfExists(targetPath);
                    Files.move(Paths.get(encryptedPath), targetPath);
                    encrypted = true;
                    log.info("文件已加密: {}", filePath);
                } else {
                    log.warn("文件加密失败，保留原文件: {}", filePath);
                }
            }

            // 构建结果
            UploadResult result = UploadResult.success("文件上传成功");
            result.setOriginalName(originalName);
            result.setStoredName(storedName);
            result.setFilePath(filePath);
            result.setFileSize(file.getSize());
            result.setContentType(file.getContentType());
            result.setFileHash(fileHash);
            result.setEncrypted(encrypted);

            log.info("文件上传完成: {}", result.getStoredName());
            return result;

        } catch (Exception e) {
            log.error("文件上传失败: {}", e.getMessage(), e);
            return UploadResult.failure("文件上传失败: " + e.getMessage());
        }
    }

    /**
     * 下载文件
     *
     * @param filePath 文件路径
     * @param fileName 文件名
     * @param encrypted 是否加密
     * @param request HTTP请求
     * @param response HTTP响应
     * @return 是否下载成功
     */
    public boolean downloadFile(String filePath, String fileName, boolean encrypted,
                                HttpServletRequest request, HttpServletResponse response) {
        try {
            log.info("开始下载文件: {}, 原名: {}, 是否加密: {}", filePath, fileName, encrypted);

            Path file = Paths.get(filePath);
            if (!Files.exists(file)) {
                log.warn("文件不存在: {}", filePath);
                response.sendError(HttpServletResponse.SC_NOT_FOUND, "文件不存在");
                return false;
            }

            // 处理断点续传
            long fileLength = Files.size(file);
            long start = 0;
            long end = fileLength - 1;

            String rangeHeader = request.getHeader("Range");
            if (rangeHeader != null && rangeHeader.startsWith("bytes=")) {
                String[] ranges = rangeHeader.substring(6).split("-");
                try {
                    start = Long.parseLong(ranges[0]);
                    if (ranges.length > 1 && !ranges[1].isEmpty()) {
                        end = Long.parseLong(ranges[1]);
                    }
                } catch (NumberFormatException e) {
                    log.warn("无效的Range头: {}", rangeHeader);
                }
            }

            // 设置响应头
            response.setContentType("application/octet-stream");
            response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\"");
            response.setHeader("Accept-Ranges", "bytes");

            long contentLength = end - start + 1;
            response.setContentLengthLong(contentLength);

            if (rangeHeader != null) {
                response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
                response.setHeader("Content-Range", "bytes " + start + "-" + end + "/" + fileLength);
            }

            // 输出文件内容
            if (encrypted) {
                return downloadEncryptedFile(filePath, start, end, response);
            } else {
                return downloadPlainFile(filePath, start, end, response);
            }

        } catch (Exception e) {
            log.error("文件下载失败: {}", e.getMessage(), e);
            return false;
        }
    }

    /**
     * 验证上传文件
     */
    private UploadResult validateFile(MultipartFile file) {
        // 检查文件是否为空
        if (file.isEmpty()) {
            return UploadResult.failure("文件不能为空");
        }

        // 检查文件大小
        if (file.getSize() > maxFileSize) {
            return UploadResult.failure("文件大小超过限制: " + (maxFileSize / 1024 / 1024) + "MB");
        }

        // 检查文件扩展名
        String originalName = file.getOriginalFilename();
        if (originalName == null || !originalName.contains(".")) {
            return UploadResult.failure("文件名无效");
        }

        String extension = FilenameUtils.getExtension(originalName).toLowerCase();
        List<String> allowedList = Arrays.asList(allowedExtensions.split(","));
        if (!allowedList.contains(extension)) {
            return UploadResult.failure("不支持的文件类型: " + extension);
        }

        return UploadResult.success("文件验证通过");
    }

    /**
     * 生成存储文件名
     */
    private String generateStoredName(String extension) {
        return UUID.randomUUID().toString().replace("-", "") + "." + extension;
    }

    /**
     * 创建用户目录
     */
    private String createUserDirectory(Long userId) throws IOException {
        String userDir = basePath + "users" + File.separator + "user_" + userId;
        Path userPath = Paths.get(userDir);

        if (!Files.exists(userPath)) {
            Files.createDirectories(userPath);
            log.info("创建用户目录: {}", userDir);
        }

        return userDir;
    }

    /**
     * 下载普通文件
     */
    private boolean downloadPlainFile(String filePath, long start, long end, HttpServletResponse response) {
        try (RandomAccessFile file = new RandomAccessFile(filePath, "r");
             OutputStream os = response.getOutputStream()) {

            file.seek(start);
            byte[] buffer = new byte[BUFFER_SIZE];
            long remaining = end - start + 1;

            while (remaining > 0) {
                int bytesToRead = (int) Math.min(buffer.length, remaining);
                int bytesRead = file.read(buffer, 0, bytesToRead);

                if (bytesRead == -1) break;

                os.write(buffer, 0, bytesRead);
                remaining -= bytesRead;
            }

            os.flush();
            log.info("普通文件下载完成: {}", filePath);
            return true;

        } catch (Exception e) {
            log.error("普通文件下载失败: {}", e.getMessage(), e);
            return false;
        }
    }

    /**
     * 下载加密文件
     */
    private boolean downloadEncryptedFile(String filePath, long start, long end, HttpServletResponse response) {
        try {
            // 临时解密文件
            String tempFile = filePath + TEMP_SUFFIX;
            if (!encryptionService.decryptFile(filePath, tempFile)) {
                log.error("解密文件失败: {}", filePath);
                return false;
            }

            // 下载解密后的文件
            boolean success = downloadPlainFile(tempFile, start, end, response);

            // 清理临时文件
            try {
                Files.deleteIfExists(Paths.get(tempFile));
            } catch (Exception e) {
                log.warn("清理临时文件失败: {}", tempFile);
            }

            if (success) {
                log.info("加密文件下载完成: {}", filePath);
            }

            return success;

        } catch (Exception e) {
            log.error("加密文件下载失败: {}", e.getMessage(), e);
            return false;
        }
    }
}